﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Threading;
using System.Xml.Serialization;
using MartinsEssentials.MVVM;
using MyAnimelistRecommender.DataObject;

namespace MyAnimelistRecommender
{
    public static class ObservableExtender
    {
        public static void Sort<TSource, TKey>(this ObservableCollection<TSource> source, Func<TSource, TKey> keySelector)
        {
            if (source == null) return;

            Comparer<TKey> comparer = Comparer<TKey>.Default;

            for (int i = source.Count - 1; i >= 0; i--)
            {
                for (int j = 1; j <= i; j++)
                {
                    TSource o1 = source[j - 1];
                    TSource o2 = source[j];
                    if (comparer.Compare(keySelector(o1), keySelector(o2)) > 0)
                    {
                        source.Remove(o1);
                        source.Insert(j, o1);
                    }
                }
            }
        }
    }

    public class RecommendationModel : ViewModelBase
    {
        #region private fields

        private int _AnimeCount;

        //private List<RecommendationGroup> _SRecs;
        private ObservableCollection<Anime> _AnimeList = new ObservableCollection<Anime>();

        private int _AnimeProgress;
        private ObservableCollection<string> _BlackList = new ObservableCollection<string>();
        private bool _IsBusy;
        private bool _IsVeryBusy;
        private ObservableCollection<RecommendationGroup> _Recommendations = new ObservableCollection<RecommendationGroup>();

        [XmlIgnore]
        private string _SearchText;

        [XmlIgnore]
        private Anime _SelectedAnime;

        private RecommendationGroup _SelectedRecommendation;
        private string _SelectedSortingMode;
        private Dictionary<string, Func<RecommendationGroup, object>> _SortingMode;
        private string _UserName = "martinyyyy";
        private int _Workers = 30;

        private delegate void Del(IEnumerable<Anime> x, Dispatcher disp);

        #endregion private fields

        #region constructors

        public RecommendationModel()
        {
            _Dispatcher = Dispatcher.CurrentDispatcher;
            SortingMode = new Dictionary<string, Func<RecommendationGroup, object>>()
            {
                {
                    "Recommends",
                    new Func<RecommendationGroup, object>(x=>x.Votes)
                },
                {
                    "Scored Recommends",
                    new Func<RecommendationGroup, object>(x=>x.CalcedVotes)
                },
                {
                    "Sources",
                    new Func<RecommendationGroup, object>(x=>x.Sources)
                }
            };
            SelectedSortingMode = SortingMode.Keys.First();
        }

        #endregion constructors

        #region public properties

        private string _SelectedGenres;

        public int AnimeCount
        {
            get
            {
                return _AnimeCount;
            }
            set
            {
                if (value != _AnimeCount)
                {
                    _AnimeCount = value;
                    OnPropertyChanged("AnimeCount");
                }
            }
        }

        public ObservableCollection<Anime> AnimeList
        {
            get
            {
                return _AnimeList;
            }
            set
            {
                if (value != _AnimeList)
                {
                    _AnimeList = value;
                    OnPropertyChanged("AnimeList");
                }
            }
        }

        public int AnimeProgress
        {
            get
            {
                return _AnimeProgress;
            }
            set
            {
                if (value != _AnimeProgress)
                {
                    _AnimeProgress = value;
                    OnPropertyChanged("AnimeProgress");
                }
            }
        }

        public ObservableCollection<string> BlackList
        {
            get
            {
                return _BlackList;
            }
            set
            {
                if (value != _BlackList)
                {
                    _BlackList = value;
                    OnPropertyChanged("BlackList");
                }
            }
        }

        public bool IsBusy
        {
            get
            {
                return _IsBusy;
            }
            set
            {
                if (value != _IsBusy)
                {
                    _IsBusy = value;
                    OnPropertyChanged("IsBusy");
                    OnPropertyChanged("IsNotBusy");
                }
            }
        }

        public bool IsNotBusy
        {
            get
            {
                return !IsBusy;
            }
            set
            {
                IsBusy = !value;
            }
        }

        public bool IsNotVeryBusy
        {
            get
            {
                return !IsVeryBusy;
            }
            set
            {
                IsVeryBusy = !value;
            }
        }

        public bool IsVeryBusy
        {
            get
            {
                return _IsVeryBusy;
            }
            set
            {
                if (value != _IsVeryBusy)
                {
                    _IsVeryBusy = value;
                    OnPropertyChanged("IsVeryBusy");
                    OnPropertyChanged("IsNotVeryBusy");
                }
            }
        }

        [XmlIgnore]
        public ObservableCollection<RecommendationGroup> Recommendations
        {
            get
            {
                return _Recommendations;
            }
            set
            {
                if (value != _Recommendations)
                {
                    _Recommendations = value;
                    OnPropertyChanged("Recommendations");
                }
            }
        }

        [XmlIgnore]
        public string SearchText
        {
            get
            {
                return _SearchText ?? "";
            }
            set
            {
                if (value != _SearchText)
                {
                    _SearchText = value;
                    OnPropertyChanged("SearchText");
                    if (Dispatcher.CurrentDispatcher != _Dispatcher)
                    {
                        _Dispatcher.Invoke((Action)delegate { DataHelper.RecommendationDataHelper.CalcBestAnime(this); });
                    }
                    else
                    {
                        DataHelper.RecommendationDataHelper.CalcBestAnime(this);
                    }
                }
            }
        }

        [XmlIgnore]
        public Anime SelectedAnime
        {
            get
            {
                return _SelectedAnime;
            }
            set
            {
                if (value != _SelectedAnime)
                {
                    _SelectedAnime = value;
                    OnPropertyChanged("SelectedAnime");
                }
            }
        }

        public string SelectedGenres
        {
            get
            {
                return _SelectedGenres;
            }
            set
            {
                if (value != _SelectedGenres)
                {
                    _SelectedGenres = value;
                    OnPropertyChanged("SelectedGenres");
                }
            }
        }

        [XmlIgnore]
        public RecommendationGroup SelectedRecommendation
        {
            get
            {
                return _SelectedRecommendation;
            }
            set
            {
                if (value != _SelectedRecommendation)
                {
                    _SelectedRecommendation = value;
                    OnPropertyChanged("SelectedRecommendation");
                    DataHelper.RecommendationDataHelper.LoadExtendedData(App.Model, _SelectedRecommendation);
                }
            }
        }

        public string SelectedSortingMode
        {
            get
            {
                return _SelectedSortingMode;
            }
            set
            {
                if (value != _SelectedSortingMode)
                {
                    _SelectedSortingMode = value;
                    OnPropertyChanged("SelectedSortingMode");
                    DataHelper.RecommendationDataHelper.CalcBestAnime(this);
                }
            }
        }

        [XmlIgnore]
        public Dictionary<string, System.Func<RecommendationGroup, object>> SortingMode
        {
            get
            {
                return _SortingMode;
            }
            set
            {
                if (value != _SortingMode)
                {
                    _SortingMode = value;
                    OnPropertyChanged("SortingMode");
                }
            }
        }

        public string UserName
        {
            get
            {
                return _UserName;
            }
            set
            {
                if (value != _UserName)
                {
                    _UserName = value;
                    OnPropertyChanged("UserName");
                }
            }
        }

        public int Workers
        {
            get
            {
                return _Workers;
            }
            set
            {
                if (value != _Workers)
                {
                    _Workers = value;
                    OnPropertyChanged("Workers");
                }
            }
        }

        #endregion public properties

        #region private methods

        #endregion private methods

        #region public methods

        #endregion public methods

        private Dictionary<string, RecommendationGroup> _RecommendationDict;
        private Dispatcher _Dispatcher;

        public void UpdateRecommendations(List<RecommendationGroup> list)
        {
            if (_RecommendationDict == null)
            {
                _RecommendationDict = new Dictionary<string, RecommendationGroup>();
            }
            var listDict = list.ToDictionary(x => x.Name);
            foreach (var i in list)
            {
                if (_RecommendationDict.ContainsKey(i.Name))
                {
                    var up = _RecommendationDict[i.Name];
                    up.Aid = i.Aid;
                    up.CalcedVotes = i.CalcedVotes;
                    up.Genres = i.Genres;
                    up.Image = i.Image;
                    up.IsEnglish = i.IsEnglish;
                    up.Langs = i.Langs;
                    up.MaxRecs = i.MaxRecs;
                    up.MemberScore = i.MemberScore;
                    up.Name = i.Name;
                    up.Place = i.Place;
                    up.Rank = i.Rank;
                    up.RecNamesAndScore = i.RecNamesAndScore;
                    up.Sources = i.Sources;
                    up.Uri = i.Uri;
                    up.Votes = i.Votes;
                }
                else
                {
                    _RecommendationDict.Add(i.Name, i);
                    Recommendations.Add(i);
                }
            }
            foreach (var i in _RecommendationDict.Keys.ToList())
            {
                if (!listDict.ContainsKey(i))
                {
                    Recommendations.Remove(_RecommendationDict[i]);
                    _RecommendationDict.Remove(i);
                }
            }

            for (int i = 0; i < list.Count; i++)
            {
                Recommendations.Move(Recommendations.IndexOf(_RecommendationDict[list[i].Name]), i);
            }
        }
    }
}